Previous Book Contents Book Index Next

Inside Macintosh: Overview /
Chapter 5 - Drawing


Drawing Shapes

As you've seen, you can draw circles by calling FrameOval. The Venn Diagrammer application uses code like this to draw the outlines of the five circles:

FOR count := 1 TO 5 DO
   FrameOval(gGeometry^^.circleRects[count]);
The rectangles defining the circles are stored in an array of rectangles that is one of the fields of an application-defined data structure of type MyGeometryRec. Venn Diagrammer allocates just one of these records when the application first starts up. The global variable gGeometry is a handle to that record.

VAR
   gGeometry:  MyGeometryHnd;       {handle to a geometry record}
Listing 5-2 shows part of the structure of this record.

Listing 5-2 The structure of a record describing a document window's geometry

TYPE MyGeometryRec =
   RECORD
      circleRects:   ARRAY[1..5] OF Rect;       {squares for the 5 circles}
      circleRgns:    ARRAY[1..5] OF RgnHandle;  {regions for the 5 circles}
      premiseRgns:   ARRAY[1..8] OF RgnHandle;  {regions for premises}
      concRgns:      ARRAY[1..4] OF RgnHandle;  {regions for conclusion}
      {other fields omitted}
   END;
   MyGeometryPtr = ^MyGeometryRec;
   MyGeometryHnd = ^MyGeometryPtr;
This record contains all the information needed to perform graphics operations on the Venn diagram in a document window. The fields are initialized at application launch time by the application-defined routine DoInitGeometry, shown in Listing 5-3.

Listing 5-3 Initializing the geometry record

PROCEDURE DoInitGeometry;
BEGIN
   {Allocate the memory needed to hold the diagram's geometry.}
   gGeometry := MyGeometryHnd(NewHandleClear(sizeof(MyGeometryRec)));

   IF gGeometry = NIL THEN                   {make sure we have the memory}
      DoBadError(eNotEnoughMemory);          {see Listing 9-5 on page 178}

   {Set up the rectangles that define the circles.}
   FOR count := 1 TO 5 DO
      gGeometry^^.circleRects[count] := MyGetIndCircleRect(count);

   {Set up the regions that the circles define.}
   DoSetupCircleRegions;

   {Set up the overlapping regions within the circles.}
   DoSetupOverlapRegions;
END;
The DoInitGeometry procedure allocates a geometry record and calls other application-defined routines to initialize the fields of that record. First, it calls MyGetIndCircleRect to determine the rectangle bounding each of the five circles.

Note
The MyGetIndCircleRect function is not defined in this book. You could define such a function in many ways. You could determine in advance where in the window the five rectangles should be and then hard-code that information in constants. Alternatively, you could calculate desirable positions dynamically at run time. The Venn Diagrammer application uses the first method, for speed.
Then DoInitGeometry calls two other application-defined routines to set up a number of regions in the window. The first, DoSetupCircleRegions, defined in Listing 5-4, creates regions corresponding to the area inside each of the five circles. These regions are used in turn by the DoSetupOverlapRegions procedure to calculate the regions of intersection.

Listing 5-4 Defining circular regions

PROCEDURE DoSetupCircleRegions;
VAR
   count:   Integer;
BEGIN
   FOR count := 1 TO 5 DO
   BEGIN
      gGeometry^^.circleRgns[count] := NewRgn;
      OpenRgn;
      FrameOval(gGeometry^^.circleRects[count]);
      CloseRgn(gGeometry^^.circleRgns[count]);
   END;
END;
You create a new region by calling the NewRgn function, which allocates storage in your application heap for a structure of type Region and returns a handle (of type RgnHandle) to that region. The newly created region is empty. To add to the region, you call the OpenRgn procedure and then draw the outline of the area you want enclosed by the region. As you can see, DoSetupCircleRegions indicates the desired area by calling the FrameOval procedure on a circle's defining rectangle. When you're done drawing that outline, you call the CloseRgn procedure, passing it a handle to the region to close.

If you simply want to create a region that's empty, you can call NewRgn, OpenRgn, and CloseRgn without doing any drawing.

myRegion := NewRgn;              {create an empty region}
OpenRgn;
CloseRgn(myRegion);
The DoSetupOverlapRegions procedure, defined in Listing 5-5, uses the circular regions defined by DoSetupCircleRegions to define the regions corresponding to the areas defined by the overlapping circles.

Listing 5-5 Defining noncircular regions

PROCEDURE DoSetupOverlapRegions;
VAR
   myRegion:   RgnHandle;           {a scratch region}
   count:      Integer;
BEGIN
   FOR count := 1 TO 8 DO           {create new, empty regions}
      BEGIN
         gGeometry^^.premiseRgns[count] := NewRgn;
         OpenRgn;
         CloseRgn(gGeometry^^.premiseRgns[count]);
      END;

   myRegion := NewRgn;              {create a scratch region}
   OpenRgn;
   CloseRgn(myRegion);

   {Calculate the overlap regions in the premises diagram.}
   HLock(Handle(gGeometry));           {lock the handle}
   WITH gGeometry^^ DO
      BEGIN
         DiffRgn(circleRgns[1], circleRgns[2], myRegion);
         DiffRgn(myRegion, circleRgns[3], premiseRgns[1]);

         SectRgn(circleRgns[1], circleRgns[2], myRegion);
         DiffRgn(myRegion, circleRgns[3], premiseRgns[2]);

         DiffRgn(circleRgns[2], circleRgns[1], myRegion);
         DiffRgn(myRegion, circleRgns[3], premiseRgns[3]);

         SectRgn(circleRgns[1], circleRgns[3], myRegion);
         DiffRgn(myRegion, circleRgns[2], premiseRgns[4]);

         SectRgn(circleRgns[1], circleRgns[2], myRegion);
         SectRgn(myRegion, circleRgns[3], premiseRgns[5]);

         SectRgn(circleRgns[2], circleRgns[3], myRegion);
         DiffRgn(myRegion, circleRgns[1], premiseRgns[6]);

         DiffRgn(circleRgns[3], circleRgns[1], myRegion);
         DiffRgn(myRegion, circleRgns[2], premiseRgns[7]);
      END;

   HUnlock(Handle(gGeometry));      {unlock the handle}
   DisposeRgn(myRegion);            {dispose scratch region}
END;
The DoSetupOverlapRegions procedure is remarkably straightforward. It initializes the regions in the premises diagram and also creates a temporary scratch region. Then it calculates the seven regions of overlap in that diagram by calling SectRgn and DiffRgn on the circular regions defined in Listing 5-4. The SectRgn procedure takes the intersection of two regions and places it into a third region. The DiffRgn procedure takes the portion of the first region that is outside the second region and places it into the third region. Figure 5-7 shows how the overlap regions are defined by taking intersections and unions of the three circles.

Figure 5-7 Calculating the overlap regions of a Venn diagram

Note
The definition of DoSetupOverlapRegions given in Listing 5-5 is
not complete. It omits calculations of the conclusion regions and of the fields omitted from the MyGeometryRec data structure defined in Listing 5-2.
Now that the Venn Diagrammer application has defined the various regions in the Venn diagram, it's easy to draw in those regions. For instance, to shade the very center of the diagram, you could call the FillRgn procedure, as follows:

FillRgn(gGeometry^^.premiseRgns[5], gEmptyPats[gEmptyIndex]^^);
This fills the specified region with the current emptiness pattern.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
9 JUL 1996